home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / TURBOPASCAL WIN / OWLDEMOS.PAK / CALC.PAS < prev    next >
Pascal/Delphi Source File  |  1992-06-08  |  10KB  |  382 lines

  1. {************************************************}
  2. {                                                }
  3. {   Turbo Pascal for Windows                     }
  4. {   Demo program                                 }
  5. {   Copyright (c) 1992 by Borland International  }
  6. {                                                }
  7. {************************************************}
  8.  
  9. { Simple four function calculator }
  10.  
  11. program Calc;
  12.  
  13. {$B-}
  14. {$R CALC.RES}
  15.  
  16. uses WObjects, WinTypes, WinProcs, Strings;
  17.  
  18. const
  19.  
  20. { Application name }
  21.  
  22.   AppName: PChar = 'Calc';
  23.  
  24. { Number of digits in calculator display }
  25.  
  26.   DisplayDigits = 15;
  27.  
  28. { Control ID of display static text }
  29.  
  30.   id_Display = 400;
  31.  
  32. { Color constants }
  33.  
  34.   rgb_Yellow = $0000FFFF;
  35.   rgb_Blue   = $00FF0000;
  36.   rgb_Red    = $000000FF;
  37.  
  38. type
  39.  
  40. { Calculator state }
  41.  
  42.   TCalcState = (cs_First, cs_Valid, cs_Error);
  43.  
  44. { Calculator dialog window object }
  45.  
  46.   PCalc = ^TCalc;
  47.   TCalc = object(TDlgWindow)
  48.     CalcStatus: TCalcState;
  49.     Number: array[0..DisplayDigits] of Char;
  50.     Negative: Boolean;
  51.     Operator: Char;
  52.     Operand: Real;
  53.     BlueBrush: HBrush;
  54.     constructor Init;
  55.     destructor Done; virtual;
  56.     function GetClassName: PChar; virtual;
  57.     procedure GetWindowClass(var AWndClass: TWndClass); virtual;
  58.     procedure WMControlColor(var Msg: TMessage);
  59.       virtual wm_First + wm_CtlColor;
  60.     procedure WMPaint(var Msg: TMessage);
  61.       virtual wm_First + wm_Paint;
  62.     procedure DefChildProc(var Msg: TMessage); virtual;
  63.     procedure DefCommandProc(var Msg: TMessage); virtual;
  64.     procedure FlashButton(Key: Char);
  65.     procedure CalcKey(Key: Char);
  66.     procedure Clear;
  67.     procedure UpdateDisplay; virtual;
  68.   end;
  69.  
  70. { Calculator application object }
  71.  
  72.   TCalcApp = object(TApplication)
  73.     procedure InitMainWindow; virtual;
  74.     procedure InitInstance; virtual;
  75.     function ProcessAppMsg(var Message: TMsg) : Boolean; virtual;
  76.   end;
  77.  
  78. var
  79.  
  80. { Application instance }
  81.  
  82.   CalcApp: TCalcApp;
  83.  
  84. { Calculator constructor.  Create blue brush for calculator background,
  85.   and do a clear command. }
  86.  
  87. constructor TCalc.Init;
  88. begin
  89.   TDlgWindow.Init(nil, AppName);
  90.   BlueBrush := CreateSolidBrush(rgb_Blue);
  91.   Clear;
  92. end;
  93.  
  94. { Calculator destructor.  Dispose the background brush. }
  95.  
  96. destructor TCalc.Done;
  97. begin
  98.   DeleteObject(BlueBrush);
  99.   TDlgWindow.Done;
  100. end;
  101.  
  102. { We're changing the window class so we must supply a new class name. }
  103.  
  104. function TCalc.GetClassName: PChar;
  105. begin
  106.   GetClassName := AppName;
  107. end;
  108.  
  109. { The calculator has its own icon which is installed here. }
  110.  
  111. procedure TCalc.GetWindowClass(var AWndClass: TWndClass);
  112. begin
  113.   TDlgWindow.GetWindowClass(AWndClass);
  114.   AWndClass.hIcon := LoadIcon(HInstance, AppName);
  115. end;
  116.  
  117. { Colorize the calculator.  Allows background to show through corners of
  118.   buttons, uses yellow text on black background in the display, and sets
  119.   the dialog background to blue. }
  120.  
  121. procedure TCalc.WMControlColor(var Msg: TMessage);
  122. begin
  123.   case Msg.LParamHi of
  124.     ctlColor_Btn:
  125.       Msg.Result := GetStockObject(null_Brush);
  126.     ctlColor_Static:
  127.       begin
  128.         SetTextColor(Msg.WParam, rgb_Yellow);
  129.         SetBkMode(Msg.WParam, transparent);
  130.         Msg.Result := GetStockObject(black_Brush);
  131.       end;
  132.     ctlcolor_Dlg:
  133.       begin
  134.         SetBkMode(Msg.WParam, Transparent);
  135.         Msg.Result := BlueBrush;
  136.       end;
  137.   else
  138.     DefWndProc(Msg);
  139.   end;
  140. end;
  141.  
  142. { Even dialogs can have their background's painted on.  This creates
  143.   a red ellipse over the blue background. }
  144.  
  145. procedure TCalc.WMPaint(var Msg: TMessage);
  146. var
  147.   OldBrush: HBrush;
  148.   OldPen: HPen;
  149.   R: TRect;
  150.   PS: TPaintStruct;
  151. begin
  152.   BeginPaint(HWindow, PS);
  153.   OldBrush := SelectObject(PS.hdc, CreateSolidBrush(rgb_Red));
  154.   OldPen := SelectObject(PS.hdc, GetStockObject(null_Pen));
  155.   GetClientRect(HWindow, R);
  156.   R.bottom := R.right;
  157.   OffsetRect(R, -R.right div 4, -R.right div 4);
  158.   Ellipse(PS.hdc, R.left, R.top, R.right, R.bottom);
  159.   SelectObject(PS.hdc, OldPen);
  160.   DeleteObject(SelectObject(PS.hdc, OldBrush));
  161.   EndPaint(HWindow, PS);
  162. end;
  163.  
  164. { Flash a button with the value of Key.  Looks exactly like a
  165.   click of the button with the mouse. }
  166.  
  167. procedure TCalc.FlashButton(Key: Char);
  168. var
  169.   Button: HWnd;
  170.   Delay: Word;
  171. begin
  172.   if Key = #13 then Key := '=';
  173.   Button := GetDlgItem(HWindow, Integer(UpCase(Key)));
  174.   if Button <> 0 then
  175.   begin
  176.     SendMessage(Button, bm_SetState, 1, 0);
  177.     for Delay := 1 to 30000 do;
  178.     SendMessage(Button, bm_SetState, 0, 0);
  179.   end;
  180. end;
  181.  
  182. { Rather then handle each button individually with child ID
  183.   response methods, it is possible to handle them all at
  184.   once with the default child procedure. }
  185.  
  186. procedure TCalc.DefChildProc(var Msg: TMessage);
  187. begin
  188.   if (Msg.WParamHi = 0) and (Msg.LParamHi = bn_Clicked) then
  189.     CalcKey(Char(Msg.WParamLo));
  190.   TDlgWindow.DefChildProc(Msg);
  191. end;
  192.  
  193. { Rather then handle each accelerator individually with
  194.   command ID response methods, it is possible to handle them
  195.   all at once with the default command procedure. }
  196.  
  197. procedure TCalc.DefCommandProc(var Msg: TMessage);
  198. begin
  199.   if Msg.WParamHi = 0 then
  200.   begin
  201.     FlashButton(Char(Msg.WParamLo)); { flash button as if it were pushed }
  202.     CalcKey(Char(Msg.WParamLo));
  203.   end;
  204.   TDlgWindow.DefCommandProc(Msg);
  205. end;
  206.  
  207. { Set Display text to the current value. }
  208.  
  209. procedure TCalc.UpdateDisplay;
  210. var
  211.   S: array[0..DisplayDigits + 1] of Char;
  212. begin
  213.   if Negative then StrCopy(S, '-') else S[0] := #0;
  214.   SetWindowText(GetDlgItem(HWindow, id_Display), StrCat(S, Number));
  215. end;
  216.  
  217. { Clear the calculator. }
  218.  
  219. procedure TCalc.Clear;
  220. begin
  221.   CalcStatus := cs_First;
  222.   StrCopy(Number, '0');
  223.   Negative := False;
  224.   Operator := '=';
  225. end;
  226.  
  227. { Process calculator key. }
  228.  
  229. procedure TCalc.CalcKey(Key: Char);
  230. var
  231.   R: Real;
  232.  
  233.   procedure Error;
  234.   begin
  235.     CalcStatus := cs_Error;
  236.     StrCopy(Number, 'Error');
  237.     Negative := False;
  238.   end;
  239.  
  240.   procedure SetDisplay(R: Real);
  241.   var
  242.     First, Last: PChar;
  243.     S: array[0..63] of Char;
  244.   begin
  245.     Str(R: 0: 10, S);
  246.     First := S;
  247.     Negative := False;
  248.     if S[0] = '-' then
  249.     begin
  250.       Inc(First);
  251.       Negative := True;
  252.     end;
  253.     if StrLen(First) > DisplayDigits + 1 + 10 then Error else
  254.     begin
  255.       Last := StrEnd(First);
  256.       while Last[Word(-1)] = '0' do Dec(Last);
  257.       if Last[Word(-1)] = '.' then Dec(Last);
  258.       StrLCopy(Number, First, Last - First);
  259.     end;
  260.   end;
  261.  
  262.   procedure GetDisplay(var R: Real);
  263.   var
  264.     E: Integer;
  265.   begin
  266.     Val(Number, R, E);
  267.     if Negative then R := -R;
  268.   end;
  269.  
  270.   procedure CheckFirst;
  271.   begin
  272.     if CalcStatus = cs_First then
  273.     begin
  274.       CalcStatus := cs_Valid;
  275.       StrCopy(Number, '0');
  276.       Negative := False;
  277.     end;
  278.   end;
  279.  
  280.   procedure InsertKey;
  281.   var
  282.     L: Integer;
  283.   begin
  284.     L := StrLen(Number);
  285.     if L < DisplayDigits then
  286.     begin
  287.       Number[L] := Key;
  288.       Number[L + 1] := #0;
  289.     end;
  290.   end;
  291.  
  292. begin
  293.   Key := UpCase(Key);
  294.   if (CalcStatus = cs_Error) and (Key <> 'C') then Key := ' ';
  295.   case Key of
  296.     '0'..'9':
  297.       begin
  298.         CheckFirst;
  299.         if StrComp(Number, '0') = 0 then Number[0] := #0;
  300.         InsertKey;
  301.       end;
  302.     '.':
  303.       begin
  304.         CheckFirst;
  305.         if StrPos(Number, '.') = nil then InsertKey;
  306.       end;
  307.     #8:
  308.       begin
  309.         CheckFirst;
  310.         if StrLen(Number) = 1 then StrCopy(Number, '0')
  311.         else Number[StrLen(Number) - 1] := #0;
  312.       end;
  313.     '_':
  314.       Negative := not Negative;
  315.     '+', '-', '*', '/', '=', '%', #13:
  316.       begin
  317.         if CalcStatus = cs_Valid then
  318.         begin
  319.           CalcStatus := cs_First;
  320.           GetDisplay(R);
  321.           if Key = '%' then
  322.             case Operator of
  323.               '+', '-': R := Operand * R / 100;
  324.               '*', '/': R := R / 100;
  325.             end;
  326.           case Operator of
  327.             '+': SetDisplay(Operand + R);
  328.             '-': SetDisplay(Operand - R);
  329.             '*': SetDisplay(Operand * R);
  330.             '/': if R = 0 then Error else SetDisplay(Operand / R);
  331.           end;
  332.         end;
  333.         Operator := Key;
  334.         GetDisplay(Operand);
  335.       end;
  336.     'C':
  337.       Clear;
  338.   end;
  339.   UpdateDisplay;
  340. end;
  341.  
  342. { Create calculator as the application's main window. }
  343.  
  344. procedure TCalcApp.InitMainWindow;
  345. begin
  346.   MainWindow := New(PCalc, Init);
  347. end;
  348.  
  349. { This application loads accelerators so that key input can be used. }
  350.  
  351. procedure TCalcApp.InitInstance;
  352. begin
  353.   TApplication.InitInstance;
  354.   HAccTable := LoadAccelerators(HInstance, AppName);
  355. end;
  356.  
  357. { This is one of the few places where the order of processing of
  358.   messages is important.  The usual order, ProcessDlgMsg,
  359.   ProcessMDIAccels, ProcessAccels, allows an application to define
  360.   accelerators which will not break the keyboard handling in
  361.   child dialogs.  In this case, the dialog is the application.
  362.   If we used the default ProcessAppMsg, then the keyboard
  363.   handler, ProcessDlgMsg, would return true and accelerators
  364.   would not be processed.  In this case, what we are doing is safe
  365.   because we are not defining any accelerators which conflict
  366.   with the Window's keyboard handling for dialogs.  Making this
  367.   change allows us to use keyboard input of the calculator.  Also,
  368.   because this is our app, we know that it is not an MDI app,
  369.   therefore we do not need to call ProcessMDIAccels (although it
  370.   would not hurt to do so). }
  371.  
  372. function TCalcApp.ProcessAppMsg(var Message: TMsg): Boolean;
  373. begin
  374.   ProcessAppMsg := ProcessAccels(Message) or ProcessDlgMsg(Message);
  375. end;
  376.  
  377. begin
  378.   CalcApp.Init(AppName);
  379.   CalcApp.Run;
  380.   CalcApp.Done;
  381. end.
  382.